package org.deng.learn.mybatis.java;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.deng.learn.mybatis.interceptor.MyInterceptor;
import org.deng.learn.mybatis.java.mapper.UserMapper2;
import tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.mapperhelper.MapperHelper;


import javax.sql.DataSource;
import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.util.Properties;

/**
 * @Author DengLibin
 * @Date 19-4-27
 * @Time 下午6:57
 * @Desc 单例工厂
 */
public class SingletonFactory {

    private static class Inner{
        private Inner(){}

        /**
         * SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在，没有任何理由丢弃它或重新创建另一个实例。 \
         * 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次，多次重建 SqlSessionFactory 被视为一种代码“坏味道（bad smell）”。
         * 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到，最简单的就是使用单例模式或者静态单例模式。
         */
        private static SqlSessionFactory sessionFactory;
        static {
            try{
                //读取数据库配置
                Reader reader = Resources.getResourceAsReader("db.properties");
                //加载到properties
                Properties properties = new Properties();
                properties.load(reader);
                //创建DataSource
                DruidDataSource dataSource = new DruidDataSource();
                dataSource.setUrl(properties.getProperty("mysql.url"));
                dataSource.setUsername(properties.getProperty("mysql.username"));
                dataSource.setPassword(properties.getProperty("mysql.password"));
                dataSource.setDriverClassName(properties.getProperty("mysql.driver"));

                //创建环境
                Environment environment = new Environment("environment", new JdbcTransactionFactory(), dataSource);
                //创建配置
                Configuration configuration = new Configuration(environment);
                configuration.setCallSettersOnNulls(true);//null值也映射 指定当结果集中值为 null 的时候是否调用映射对象的 setter（map 对象时为 put）方法，这在依赖于 Map.keySet() 或 null 值初始化的时候比较有用。注意基本类型（int、boolean 等）是不能设置成 null 的。
                //configuration.addMapper(UserMapper.class); //添加mapper
                configuration.addMappers("org.deng.learn.mybatis.java.mapper"); //添加多个 直接指定包名
                configuration.addInterceptor(new MyInterceptor()); //添加拦截器
                configuration.setUseGeneratedKeys(true); //允许 JDBC 支持自动生成主键，需要驱动支持。 如果设置为 true (默认false)则这个设置强制使用自动生成主键，尽管一些驱动不能支持但仍可正常工作（比如 Derby）
                //configuration.setDefaultExecutorType();执行器类型默认 org.apache.ibatis.executor.SimpleExecutor
                configuration.setDefaultStatementTimeout(30);//设置超时时间，它决定驱动等待数据库响应的秒数。默认未设置
                configuration.setMapUnderscoreToCamelCase(true); //是否开启自动驼峰命名规则（camel case）映射，即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 默认false
               // configuration.setLocalCacheScope(LocalCacheScope.SESSION); // MyBatis 利用本地缓存机制（Local Cache）防止循环引用（circular references）和加速重复嵌套查询 默认 SESSION

                //....其他配置项

                //创建SqlSessionFactory
                sessionFactory = new SqlSessionFactoryBuilder().build(configuration);



                /**
                 * 使用通用mapper (方式1 使用 tk.mybatis.mapper.session.Configuration 替代mybatis的配置)
 /*                *//*
                tk.mybatis.mapper.session.Configuration configuration2 = new tk.mybatis.mapper.session.Configuration();
//这里可以参考上面的方式来配置 MapperHelper
                configuration2.setMapperHelper(new MapperHelper());
                sessionFactory = new SqlSessionFactoryBuilder().build(configuration);
*/
                /**
                 * 使用通用mapper(方式2)
                 */
                //使用Mapper<T>则做如下配置

                //创建一个MapperHelper
                MapperHelper mapperHelper = new MapperHelper();
//特殊配置
                Config config = new Config();
//主键自增回写方法,默认值MYSQL,详细说明请看文档
                config.setIDENTITY("MYSQL");
//支持getter和setter方法上的注解
                config.setEnableMethodAnnotation(true);
//设置 insert 和 update 中，是否判断字符串类型!=''
                config.setNotEmpty(true);
//校验Example中的类型和最终调用时Mapper的泛型是否一致
                config.setCheckExampleEntityClass(true);
//启用简单类型
                config.setUseSimpleType(true);
//枚举按简单类型处理
                config.setEnumAsSimpleType(true);
//自动处理关键字 - mysql
                config.setWrapKeyword("`{0}`");
//设置配置
                mapperHelper.setConfig(config);
//注册通用接口，和其他集成方式中的 mappers 参数作用相同
//4.0 之后的版本，如果类似 Mapper.class 这样的基础接口带有 @RegisterMapper 注解，就不必在这里注册
                mapperHelper.registerMapper(UserMapper2.class);
//配置 mapperHelper 后，执行下面的操作
                mapperHelper.processConfiguration(configuration);


            }catch (Exception e){
                e.printStackTrace();
            }

        }
        public static  SqlSessionFactory getSqlSessionFactory(){
            return sessionFactory;
        }
    }

    /**
     * 获取sqlSessionFactory
     * @return
     */
    public static SqlSessionFactory getSqlSessionFactory(){
        return Inner.getSqlSessionFactory();
    }
}
